/*
 * @Descripttion: xxx
 * @version: 1.0.0
 * @Date: 2022-01-18 13:04:45
 * @LastEditTime: 2022-05-12 10:19:01
 */
import type { IControlItem } from "@@/apis/scn/type"
import type {
  Viewer
} from "cesium"
import type DataSource from "cesium/Source/DataSources/DataSource"
import type {
  GCS,
  IDrawCallback,
  IPoint2D,
  MouseHandler
} from "../CustomCesium"
import {
  CallbackProperty,
  Cartesian2,
  Cartesian3,
  Rectangle as CesiumRectangle,
  Color,
  ConstantPositionProperty,
  ConstantProperty,
  createGuid,
  CustomDataSource,
  DataSourceCollection,
  defined,
  Entity,
  HeightReference,
  HorizontalOrigin,
  JulianDate,
  LabelStyle,
  PolygonHierarchy,
  PolylineDashMaterialProperty
} from "cesium"
import _ from "lodash"
import {
  DrawingType
} from "../CustomCesium"
import DrawUtils from "./DrawUtisl"
import MathUtils from "./MathUtils"

export default class DrawingManager {
  private viewer: Viewer
  private mouseHandlerManager: MouseHandler
  readonly guid: string
  // 标绘实体集合
  private drewEntities: CustomDataSource
  // 标识点集合，包括已绘实体的顶点
  // 每个标绘的顶点，保存在同个DataSource中
  // 每个DataSource的name唯一
  // 名规则为：markPoints + 标绘guid
  private markPoints: DataSourceCollection
  // 当前标绘的顶点
  private activeMarkPositions: Cartesian3[]
  // 当前标绘实体
  private activeEntityId: undefined | string

  constructor(viewer: Viewer, mouseHandlerManager: MouseHandler) {
    this.mouseHandlerManager = mouseHandlerManager
    // 唯一id
    this.guid = createGuid()
    // 标识点
    this.markPoints = new DataSourceCollection()
    // 标识点坐标
    this.activeMarkPositions = []
    // 已绘实体
    this.drewEntities = new CustomDataSource(`drewEntities_${this.guid}`)
    // 活动entityId
    this.activeEntityId = undefined
    this.viewer = this.init(viewer)
  }

  private init(viewer: Viewer) {
    if (!defined(viewer)) throw new Error("EventListener,无效的视图对象！")
    viewer.dataSources.add(this.drewEntities)
    return viewer
  }

  /**
   * @descripttion: 启动标绘
   * @param {DrawingType} drawingType 标绘类型
   * @param {(res: IDrawCallback) => void} callback 标绘完成回调
   * @return {null}
   */
  draw(
    drawingType: DrawingType,
    label: string,
    sideColor: string,
    callback: (res: IDrawCallback) => void
  ) {
    // 标绘唯一id
    const shapeId = `drawing_${createGuid()}`
    // 标绘的顶点集合
    const markPointsDataSource = new CustomDataSource(`markPoints_${shapeId}`)

    // 添加至 顶点数据源集合
    this.markPoints.add(markPointsDataSource)
    // 添加到 viewer 渲染
    this.viewer.dataSources.add(markPointsDataSource)

    try {
      // 监听鼠标左键
      this.mouseHandlerManager.addLeftMouseClickHandler(
        `drawLeftClick_${this.guid}`,
        ({ screenPosition }) => {
          // 绘制圆、矩形类型，顶点不多于2个
          if (
            (drawingType == DrawingType.Circle
              || drawingType == DrawingType.Rectangle
              || drawingType == DrawingType.Ellipse)
            && this.activeMarkPositions.length == 1
          ) {
            return
          }

          // 贴地坐标or空间坐标
          const position
              = screenPosition
                && MathUtils.getSurfaceCartesian3(this.viewer, screenPosition)
          // 标绘顶点id
          const id = `drawing_${createGuid()}`

          // 单击开始绘制
          if (!position) return
          if (!defined(position)) return

          this.viewer.container.style.cursor = "pointer"

          // 添加标识点坐标
          this.activeMarkPositions.push(position)

          if (drawingType === DrawingType.Point) {
            // 创建点
            const point = this.createPoint(
              id,
              position.clone(),
              label,
              sideColor,
              true
            )

            this.drewEntities.entities.add(point)

            this.viewer.container.style.cursor = "default"

            callback({
              id,
              position: [position!.clone()]
            })

            // 移除左、右键单击，鼠标移动事件
            this.mouseHandlerManager.destroyHandlerByKey(
              `drawLeftClick_${this.guid}`
            )
            this.mouseHandlerManager.destroyHandlerByKey(
              `drawMouseMove_${this.guid}`
            )
            this.mouseHandlerManager.destroyHandlerByKey(
              `drawRightClick_${this.guid}`
            )

            // 清除标识点记录
            this.activeMarkPositions = []
          } else {
            // 创建标识点
            const point = this.createMarkPoint(id, position.clone(), true)
            // 绘制标识点
            markPointsDataSource.entities.add(point)
          }
        }
      )

      // 鼠标移动事件回调处理
      // 移除上一个实体，添加新实体
      const throttle = _.throttle((screenPosition: Cartesian2) => {
        // 绘画非点类型,活动实体
        if (drawingType === DrawingType.Point) return
        // 贴地坐标or空间坐标
        const id = `drawing_${createGuid()}`
        const position = MathUtils.getSurfaceCartesian3(
          this.viewer,
          screenPosition
        )

        // 若无有效坐标
        if (!position) return
        // 绘制移动实体
        if (!defined(position)) return

        // 顶点集合
        const positions = [...this.activeMarkPositions, position].map(car3 =>
          car3.clone()
        )

        // 创建当前移动实体
        const activeEntity = this.createEntityByDrawingType(
          id,
          positions,
          label,
          sideColor,
          drawingType,
          true,
          true
        )
        // 渲染活动实体
        if (!activeEntity) return
        if (!defined(activeEntity)) return

        // 清除上一个移动实体
        this.clearActiveEntity()

        // 渲染新的活动实体
        if (activeEntity) {
          activeEntity && this.viewer.entities.add(activeEntity)
          this.activeEntityId = id
        }
      }, 350)

      // 监听鼠标移动事件
      this.mouseHandlerManager.addMouseMoveHandler(
        `drawMouseMove_${this.guid}`,
        ({ screenPosition }) => {
          // 频繁触发
          // 节流处理
          screenPosition && throttle(screenPosition)
        }
      )

      // 右键结束绘画
      this.mouseHandlerManager.addRightMouseClickHandler(
        `drawRightClick_${this.guid}`,
        ({ screenPosition }) => {
          // 贴地坐标or空间坐标
          const position
            = screenPosition
              && MathUtils.getSurfaceCartesian3(this.viewer, screenPosition)

          // 无有效坐标，不能结束
          if (!position) return
          if (!defined(position)) return

          // 移除左、右键单击，鼠标移动事件
          this.mouseHandlerManager.destroyHandlerByKey(
            `drawLeftClick_${this.guid}`
          )
          this.mouseHandlerManager.destroyHandlerByKey(
            `drawMouseMove_${this.guid}`
          )
          this.mouseHandlerManager.destroyHandlerByKey(
            `drawRightClick_${this.guid}`
          )

          // 清除移动实体
          this.clearActiveEntity()
          // 修改鼠标样式
          this.viewer.container.style.cursor = "default"

          // 绘制线、圆、矩形类型，顶点不少于2个
          if (
            (drawingType == DrawingType.Polyline
              || drawingType == DrawingType.Circle
              || drawingType == DrawingType.Ellipse
              || drawingType == DrawingType.Rectangle)
            && this.activeMarkPositions.length < 1
          ) {
            // 绘制失败
            this.clearById(shapeId)
            this.activeMarkPositions = []
            // 绘制结束回调
            callback({
              id: shapeId,
              position,
              result: false
            })
            return
          }

          // 多边形标绘类型，顶点不少于3个
          if (
            drawingType == DrawingType.Polygon
            && this.activeMarkPositions.length < 2
          ) {
            // 绘制失败
            this.clearById(shapeId)
            this.activeMarkPositions = []
            // 绘制结束回调
            callback({
              id: shapeId,
              position,
              result: false
            })
            return
          }

          // 若非点类型标绘，则结束时再增加新的顶点
          if (drawingType != DrawingType.Point) {
            // 标绘顶点id
            const id = `drawing_${createGuid()}`
            // 添加标识点坐标
            this.activeMarkPositions.push(position)

            // 不需要渲染最后顶点的标绘 ssr注释
            // if (
            //   drawingType != DrawingType.Circle &&
            //   drawingType != DrawingType.Ellipse
            // ) {
            // // 创建标识点
            // const point = this.createMarkPoint(id, position.clone(), true);
            // // 绘制标识点
            // markPointsDataSource.entities.add(point);
            // }

            // 渲染最后顶点标绘
            // 创建标识点
            const point = this.createMarkPoint(id, position.clone(), true)
            // 绘制标识点
            markPointsDataSource.entities.add(point)
          }

          markPointsDataSource.show = false

          // 创建标绘实体
          const positions = this.activeMarkPositions.map(car3 =>
            car3.clone()
          )
          const lastEntity = this.createEntityByDrawingType(
            shapeId,
            positions,
            label,
            sideColor,
            drawingType,
            true
          )

          if (drawingType === DrawingType.Point) {
            // 清除标识点记录
            this.activeMarkPositions = []
          } else {
            // 渲染标绘实体
            if (!lastEntity) return
            if (!defined(lastEntity)) return
            this.drewEntities.entities.add(lastEntity)
          }

          let params: { [propName: string]: any } = {}
          if (drawingType == DrawingType.Circle) {
            params.Radius = lastEntity?.ellipse?.semiMajorAxis?._value
          }
          if (drawingType == DrawingType.Ellipse) {
            params.MajorSemiAxis = lastEntity?.ellipse?.semiMajorAxis?._value
            params.MinorSemiAxis = lastEntity?.ellipse?.semiMinorAxis?._value
          }
          // 绘制结束回调
          callback({
            id: shapeId,
            position: positions,
            param: params
          })

          // 清除标识点记录
          this.activeMarkPositions = []
        }
      )
    } catch (error) {
      console.error("drawManager:", error)
    }
  }

  /**
   * @descripttion: 创建标识点
   * @param {Cartesian3} cartesian
   * @param {*} clampToGround
   * @return {*}
   */
  private createMarkPoint(
    id: string,
    cartesian: Cartesian3,
    clampToGround = true
  ) {
    const point = new Entity({
      id,
      name: "drawMarkPoint",
      position: cartesian,
      point: {
        pixelSize: 10,
        color: Color.YELLOW,
        heightReference: clampToGround
          ? HeightReference.CLAMP_TO_GROUND
          : HeightReference.NONE
      },
      label: {
        scale: 0.4,
        outlineColor: Color.WHITE,
        outlineWidth: 1,
        style: LabelStyle.FILL_AND_OUTLINE,
        pixelOffset: new Cartesian2(0.0, 25.0)
      }
    })
    return point
  }

  /**
   * @descripttion: 创建点
   * @param {Cartesian3} cartesian
   * @param {*} clampToGround
   * @return {*}
   */
  private createPoint(
    id: string,
    cartesian: Cartesian3,
    label: string,
    sideColor: string,
    clampToGround = true
  ) {
    const point = new Entity({
      id,
      name: "drawPoint",
      position: cartesian,
      point: {
        pixelSize: 10,
        color: sideColor ? Color.fromCssColorString(sideColor) : Color.YELLOW,
        heightReference: clampToGround
          ? HeightReference.CLAMP_TO_GROUND
          : HeightReference.NONE,
        outlineColor: Color.fromCssColorString(sideColor)
      },
      label: sideColor
        ? {
            text: label,
            scale: 0.4,
            fillColor: sideColor
              ? Color.fromCssColorString(sideColor)
              : Color.WHITE,
            outlineColor: Color.WHITE,
            outlineWidth: 1,
            style: LabelStyle.FILL_AND_OUTLINE,
            pixelOffset: new Cartesian2(0.0, 15.0)
          }
        : {
            text: label,
            scale: 0.4,
            outlineColor: Color.WHITE,
            outlineWidth: 1,
            style: LabelStyle.FILL_AND_OUTLINE
          }
    })
    return point
  }

  /**
   * @descripttion: 画线
   * @param {Cartesian3} positions
   * @param {*} clampToGround
   * @return {*}
   */
  private createPolyline(
    id: string,
    positions: Cartesian3[],
    label: string,
    sideColor: string,
    clampToGround = true,
    isActive = false
  ): Entity | null {
    const lastPosition = _.cloneDeep(positions)
      .reverse()
      .find((_i, index) => index == 0)
    const lastPositionClone = lastPosition?.clone()
    const line
        = lastPositionClone
          && new Entity({
            id,
            name: "drawPolyline",
            position: lastPositionClone,
            point: isActive
              ? {
                  pixelSize: 10,
                  color: Color.YELLOW,
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                }
              : {
                  show: false
                },
            label: isActive
              ? {
                  text: "左键标绘，右键结束",
                  fillColor: Color.WHITE,
                  showBackground: true,
                  backgroundColor: Color.fromCssColorString(
                    "rgba(22, 171, 216, 0.5)"
                  ),
                  font: "14px sans-serif",
                  horizontalOrigin: HorizontalOrigin.LEFT,
                  pixelOffset: new Cartesian2(10, 0)
                }
              : {
                  text: label,
                  scale: 0.4,
                  fillColor: sideColor
                    ? Color.fromCssColorString(sideColor)
                    : Color.WHITE,
                  outlineColor: Color.WHITE,
                  outlineWidth: 1,
                  style: LabelStyle.FILL_AND_OUTLINE,
                  pixelOffset: new Cartesian2(0, 15)
                },
            polyline: {
              positions,
              width: 2,
              material: sideColor
                ? Color.fromCssColorString(sideColor)
                : Color.RED,
              clampToGround
            }
          })
    return line || null
  }

  /**
   * @descripttion: 创建矩形
   * @param {Cartesian3} positions
   * @param {*} clampToGround
   * @return {*}
   */
  private createRectangle(
    id: string,
    positions: Cartesian3[],
    label: string,
    sideColor: string,
    clampToGround = true,
    isActive = false
  ) {
    const lastPosition = _.cloneDeep(positions)
      .reverse()
      .find((_i, index) => index == 0)
    const lastPositionClone = lastPosition?.clone()
    const shape
        = lastPositionClone
          && new Entity({
            id,
            name: "drawRectangle",
            position: lastPositionClone,
            point: isActive
              ? {
                  pixelSize: 10,
                  color: Color.YELLOW,
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                }
              : {
                  show: false
                },
            label: isActive
              ? {
                  text: "左键标绘，右键结束",
                  fillColor: Color.WHITE,
                  showBackground: true,
                  backgroundColor: Color.fromCssColorString(
                    "rgba(22, 171, 216, 0.5)"
                  ),
                  font: "14px sans-serif",
                  horizontalOrigin: HorizontalOrigin.LEFT,
                  pixelOffset: new Cartesian2(10, 0)
                }
              : {
                  text: label,
                  scale: 0.4,
                  fillColor: sideColor
                    ? Color.fromCssColorString(sideColor)
                    : Color.WHITE,
                  outlineColor: Color.WHITE,
                  outlineWidth: 1,
                  style: LabelStyle.FILL_AND_OUTLINE,
                  pixelOffset: new Cartesian2(0.0, 0.0)
                },
            rectangle: isActive
              ? {}
              : positions.length < 2
                ? {}
                : {
                    coordinates: new CallbackProperty(() => {
                      const positionsClone = positions.map(car3 => car3.clone())
                      const rectangle
                      = CesiumRectangle.fromCartesianArray(positionsClone)
                      return rectangle
                    }, true),
                    material: sideColor
                      ? Color.fromCssColorString(sideColor).withAlpha(0.3)
                      : Color.SKYBLUE.withAlpha(0.3),
                    // outline: true,
                    // outlineColor: Color.SKYBLUE.withAlpha(0.8),
                    // outlineWidth: 1,
                    heightReference: clampToGround
                      ? HeightReference.CLAMP_TO_GROUND
                      : HeightReference.NONE
                  },
            polyline: {
              show: isActive,
              positions: new CallbackProperty(() => {
                const positionsClone = positions.map(car3 => car3.clone())
                const rectangle = CesiumRectangle.fromCartesianArray(positionsClone)
                const eastNorth = Cartesian3.fromRadians(
                  rectangle.east,
                  rectangle.north
                )
                const westNorth = Cartesian3.fromRadians(
                  rectangle.west,
                  rectangle.north
                )
                const eastSouth = Cartesian3.fromRadians(
                  rectangle.east,
                  rectangle.south
                )
                const westSouth = Cartesian3.fromRadians(
                  rectangle.west,
                  rectangle.south
                )

                return [eastNorth, westNorth, westSouth, eastSouth, eastNorth]
              }, true),
              width: 1,
              material: new PolylineDashMaterialProperty({
                color: Color.SKYBLUE.withAlpha(0.8)
              })
            }
          })

    if (!isActive && shape) {
      const center = DrawUtils.getRectangleCenter(positions)
      center && (shape.position = new ConstantPositionProperty(center))
    }

    return shape || null
  }

  /**
   * @descripttion: 创建多边形
   * @param {Cartesian3} positions
   * @param {*} clampToGround
   * @return {*}
   */
  private createPolygon(
    id: string,
    positions: Cartesian3[],
    label: string,
    sideColor: string,
    clampToGround = true,
    isActive = false
  ): Entity | null {
    const lastPosition = _.cloneDeep(positions)
      .reverse()
      .find((_i, index) => index == 0)
    const lastPositionClone = lastPosition?.clone()
    const shape
        = lastPositionClone
          && new Entity({
            id,
            name: "drawPolygon",
            position: lastPositionClone,
            point: isActive
              ? {
                  pixelSize: 10,
                  color: Color.YELLOW,
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                }
              : {
                  show: false
                },
            label: isActive
              ? {
                  text: "左键标绘，右键结束",
                  fillColor: Color.WHITE,
                  showBackground: true,
                  backgroundColor: Color.fromCssColorString(
                    "rgba(22, 171, 216, 0.5)"
                  ),
                  font: "14px sans-serif",
                  horizontalOrigin: HorizontalOrigin.LEFT,
                  pixelOffset: new Cartesian2(10, 0)
                }
              : {
                  text: label,
                  scale: 0.4,
                  fillColor: sideColor
                    ? Color.fromCssColorString(sideColor)
                    : Color.WHITE,
                  outlineColor: Color.WHITE,
                  outlineWidth: 1,
                  style: LabelStyle.FILL_AND_OUTLINE,
                  pixelOffset: new Cartesian2(0.0, 0.0)
                },
            polygon: isActive
              ? {}
              : {
                  hierarchy: new CallbackProperty(() => {
                    const positionsClone = positions.map(car3 => car3.clone())
                    const polygonHierarchy = new PolygonHierarchy(positionsClone)

                    return polygonHierarchy
                  }, true),
                  perPositionHeight: true,
                  material: sideColor
                    ? Color.fromCssColorString(sideColor).withAlpha(0.3)
                    : Color.SKYBLUE.withAlpha(0.3),
                  // outline: true,
                  // outlineColor: Color.SKYBLUE.withAlpha(0.8),
                  // outlineWidth: 1,
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                },
            polyline: {
              show: isActive,
              positions: new CallbackProperty(() => {
                const positionsClone
                = positions.length < 3
                  ? positions.map(car3 => car3.clone())
                  : positions
                      .concat(positions[0].clone())
                      .map(car3 => car3.clone())

                return positionsClone
              }, true),
              width: 1,
              material: new PolylineDashMaterialProperty({
                color: Color.SKYBLUE.withAlpha(0.8)
              })
            }
          })

    if (!isActive && shape) {
      const center = DrawUtils.getPolygonCenter(positions, shape)
      center && (shape.position = new ConstantPositionProperty(center))
    }

    return shape || null
  }

  /**
   * @descripttion: 创建圆形
   * @param {Cartesian3} positions
   * @param {*} clampToGround
   * @return {*}
   */
  private createCircle(
    id: string,
    positions: Cartesian3[],
    label: string,
    sideColor: string,
    clampToGround = true,
    isActive = false
  ): Entity | null {
    const firstPosition = positions.find((_i, index) => index == 0)
    const firstPositionClone = firstPosition?.clone()
    const shape
        = firstPositionClone
          && new Entity({
            id,
            name: "drawCircle",
            position: firstPositionClone,
            point: isActive
              ? {
                  pixelSize: 10,
                  color: Color.YELLOW,
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                }
              : {
                  show: false
                },
            label: isActive
              ? {
                  text: "左键标绘，右键结束",
                  fillColor: Color.WHITE,
                  showBackground: true,
                  backgroundColor: Color.fromCssColorString(
                    "rgba(22, 171, 216, 0.5)"
                  ),
                  font: "14px sans-serif",
                  horizontalOrigin: HorizontalOrigin.LEFT,
                  pixelOffset: new Cartesian2(10, 0)
                }
              : {
                  text: label,
                  scale: 0.4,
                  fillColor: sideColor
                    ? Color.fromCssColorString(sideColor)
                    : Color.WHITE,
                  outlineColor: Color.WHITE,
                  outlineWidth: 1,
                  style: LabelStyle.FILL_AND_OUTLINE,
                  pixelOffset: new Cartesian2(0.0, 0.0)
                },
            ellipse:
            positions.length == 2
              ? {
                  semiMajorAxis: Cartesian3.distance(
                    positions[0],
                    positions[1]
                  ),
                  semiMinorAxis: Cartesian3.distance(
                    positions[0],
                    positions[1]
                  ),
                  material: sideColor
                    ? Color.fromCssColorString(sideColor).withAlpha(0.3)
                    : Color.SKYBLUE.withAlpha(0.3),
                  outline: true,
                  outlineColor: Color.SKYBLUE.withAlpha(0.8),
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                }
              : {}
          })
    return shape || null
  }

  /**
   * @descripttion: 创建椭圆形
   * @param {Cartesian3} positions
   * @param {*} clampToGround
   * @return {*}
   */
  private createEllipse(
    id: string,
    positions: Cartesian3[],
    label: string,
    sideColor: string,
    clampToGround = true,
    isActive = false
  ): Entity | null {
    // 计算长短半径
    let { majorAxis, minorAxis } = DrawUtils.getEllipseRadius(positions)
    const firstPosition = positions.find((_i, index) => index == 0)
    const firstPositionClone = firstPosition?.clone()
    const shape
        = firstPositionClone
          && new Entity({
            id,
            name: "drawEllipse",
            position: firstPosition,
            point: isActive
              ? {
                  pixelSize: 10,
                  color: Color.YELLOW,
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                }
              : {
                  show: false
                },
            label: isActive
              ? {
                  text: "左键标绘，右键结束",
                  fillColor: Color.WHITE,
                  showBackground: true,
                  backgroundColor: Color.fromCssColorString(
                    "rgba(22, 171, 216, 0.5)"
                  ),
                  font: "14px sans-serif",
                  horizontalOrigin: HorizontalOrigin.LEFT,
                  pixelOffset: new Cartesian2(10, 0)
                }
              : {
                  text: label,
                  scale: 0.4,
                  fillColor: sideColor
                    ? Color.fromCssColorString(sideColor)
                    : Color.WHITE,
                  outlineColor: Color.WHITE,
                  outlineWidth: 1,
                  style: LabelStyle.FILL_AND_OUTLINE,
                  pixelOffset: new Cartesian2(0.0, 0.0)
                },
            ellipse:
            positions.length == 2
              ? {
                  semiMajorAxis: majorAxis,
                  semiMinorAxis: minorAxis,
                  material: sideColor
                    ? Color.fromCssColorString(sideColor).withAlpha(0.3)
                    : Color.SKYBLUE.withAlpha(0.3),
                  outline: true,
                  outlineColor: Color.SKYBLUE.withAlpha(0.8),
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                }
              : {}
          })
    return shape || null
  }

  /**
   * @descripttion: 创建曲线
   * @param {Cartesian3} positions
   * @param {*} clampToGround
   * @return {*}
   * @author: ssr
   */
  private createCurve(
    id: string,
    positions: Cartesian3[],
    label: string,
    sideColor: string,
    clampToGround = true,
    isActive = false
  ): Entity | null {
    // 轨迹关键点转为IPoint2D
    const lonLats: IPoint2D[] = MathUtils.car3ToLonLatArray(this.viewer, positions)
    const compLonLats: IPoint2D[] = MathUtils.getThreeBezier(lonLats, 10000)
    let curvePostions: number[] = []
    compLonLats.forEach((item) => {
      curvePostions.push(item[0])
      curvePostions.push(item[1])
    })
    const lastPosition = _.cloneDeep(positions)
      .reverse()
      .find((_i, index) => index == 0)
    const lastPositionClone = lastPosition?.clone()
    const shape
        = lastPositionClone
          && new Entity({
            id,
            name: "drawCurve",
            position: lastPositionClone,
            point: isActive
              ? {
                  pixelSize: 10,
                  color: Color.YELLOW,
                  heightReference: clampToGround
                    ? HeightReference.CLAMP_TO_GROUND
                    : HeightReference.NONE
                }
              : {
                  show: false
                },
            label: isActive
              ? {
                  text: "左键标绘，右键结束",
                  fillColor: Color.WHITE,
                  showBackground: true,
                  backgroundColor: Color.fromCssColorString(
                    "rgba(22, 171, 216, 0.5)"
                  ),
                  font: "14px sans-serif",
                  horizontalOrigin: HorizontalOrigin.LEFT,
                  pixelOffset: new Cartesian2(10, 0)
                }
              : {
                  text: label,
                  scale: 0.4,
                  fillColor: sideColor
                    ? Color.fromCssColorString(sideColor)
                    : Color.WHITE,
                  outlineColor: Color.WHITE,
                  outlineWidth: 1,
                  style: LabelStyle.FILL_AND_OUTLINE,
                  pixelOffset: new Cartesian2(0, 15)
                },
            polyline: {
              positions: Cartesian3.fromDegreesArray(curvePostions),
              width: 1,
              material: sideColor
                ? Color.fromCssColorString(sideColor)
                : Color.RED
            // clampToGround: clampToGround,
            }
          })
    return shape || null
  }

  /**
   * @descripttion: 根据绘制类型创建实体
   * @param {*}
   * @return {*}
   */
  private createEntityByDrawingType(
    id = createGuid(),
    positions: Cartesian3[],
    label: string,
    sideColor: string,
    drawingType: DrawingType,
    clampToGround = true,
    isActive = false
  ): Entity | null {
    let entity: null | Entity = null

    switch (+drawingType) {
      case DrawingType.Polyline:
        entity = this.createPolyline(
          id,
          positions,
          label,
          sideColor,
          clampToGround,
          isActive
        )
        break
      case DrawingType.Rectangle:
        entity = this.createRectangle(
          id,
          positions,
          label,
          sideColor,
          clampToGround,
          isActive
        )
        break
      case DrawingType.Polygon:
        entity = this.createPolygon(
          id,
          positions,
          label,
          sideColor,
          clampToGround,
          isActive
        )
        break
      case DrawingType.Circle:
        entity = this.createCircle(
          id,
          positions,
          label,
          sideColor,
          clampToGround,
          isActive
        )
        break
      case DrawingType.Ellipse:
        entity = this.createEllipse(
          id,
          positions,
          label,
          sideColor,
          clampToGround,
          isActive
        )
        break
      case DrawingType.Curve:
        entity = this.createCurve(
          id,
          positions,
          label,
          sideColor,
          clampToGround,
          isActive
        )
        break
      default:
        break
    }
    return entity
  }

  /**
   * @descripttion: 清除活动实体
   * @param {*}
   * @return {*}
   */
  private clearActiveEntity() {
    this.activeEntityId && this.viewer.entities.removeById(this.activeEntityId)
    this.activeEntityId = undefined
  }

  /**
   * @descripttion: 清除所有标记点
   * @param {*}
   * @return {*}
   */
  private clearMarkPoints() {
    const length = this.markPoints.length
    for (let index = 0; index < length; index++) {
      const ds = this.markPoints.get(index)
      ds.entities.removeAll()
    }
    this.markPoints.removeAll(true)
  }

  /**
   * @descripttion: 清除标绘
   * @param {string} id 标绘id
   * @return {null}
   */
  clearById(id: string) {
    // 移除顶点坐标
    const dsSet = this.markPoints.getByName(`markPoints_${id}`)
    // 清除顶点实体
    dsSet.forEach((item) => {
      item.entities.removeAll()
      this.markPoints.remove(item, true)
    })

    // 移除标绘
    this.drewEntities.entities.removeById(id)
  }

  /**
   * @descripttion: 清除所有标绘
   * @return {null}
   */
  clearAll() {
    this.clearMarkPoints()
    this.drewEntities.entities.removeAll()
  }

  /**
   * 根据id获取实体
   * @param id
   * @returns
   */
  getById(id: string) {
    return this.drewEntities.entities.getById(id)
  }

  /**
   * 初始化加载
   */
  initAddDraw(item: IControlItem, callback: (res: IDrawCallback) => void) {
    // 经纬度坐标转换为Cartesian3
    const positions: Cartesian3[] = []
    const gcs: GCS[] = JSON.parse(item.path)
    gcs.forEach((item) => {
      item.latitude = Number(item.latitude)
      item.longitude = Number(item.longitude)
      item.height = Number(item.height)
      positions.push(MathUtils.gcsToCartesian3(item))
    })

    const shapeId = `drawing_${createGuid()}`

    switch (+item.type) {
      case DrawingType.Point:
        // 创建点
        // eslint-disable-next-line no-case-declarations
        const point = this.createPoint(shapeId, positions[0].clone(), item.name, item.sideColor, true)
        this.drewEntities.entities.add(point)
        break
      case DrawingType.Polyline:
      case DrawingType.Rectangle:
      case DrawingType.Polygon:
      case DrawingType.Circle:
      case DrawingType.Ellipse:
      case DrawingType.Curve:
        // 创建标识点
        this.initAddMarkPoints(shapeId, positions)
        // 绘制图形
        this.initAddEntity(
          shapeId,
          positions,
          item.type,
          item.name,
          item.sideColor
        )
        break
      default:
        break
    }

    // 绘制结束回调
    callback({
      id: shapeId,
      position: positions
    })
  }

  /**
   * 绘制图形
   * @param shapeId
   * @param positions
   * @param type
   */
  initAddEntity(
    shapeId: string,
    positions: Cartesian3[],
    type: DrawingType,
    label: string,
    sideColor: string
  ) {
    // 创建路线
    const entity = this.createEntityByDrawingType(
      shapeId,
      positions,
      label,
      sideColor,
      type,
      true
    )

    if (!entity) return
    if (!defined(entity)) return

    this.drewEntities.entities.add(entity)
  }

  /**
   * 绘制表示点
   * @param shapeId 实体id
   * @param positions 坐标
   * @param type
   */
  initAddMarkPoints(shapeId: string, positions: Cartesian3[]) {
    // 标绘的顶点集合
    const markPointsDataSource = new CustomDataSource(`markPoints_${shapeId}`)

    // 添加至 顶点数据源集合
    this.markPoints.add(markPointsDataSource)
    // 添加到 viewer 渲染
    this.viewer.dataSources.add(markPointsDataSource)

    markPointsDataSource.show = false

    for (let i = 0; i < positions.length; i++) {
      const id = `drawing_${createGuid()}`
      // 创建标识点
      const point = this.createMarkPoint(id, positions[i].clone(), true)
      // 绘制标识点
      markPointsDataSource.entities.add(point)
    }
  }

  /**
   * 编辑顶点
   * @param id 实体id
   * @param drawingType 类型
   * @param positions 坐标
   * @param callback
   * @returns
   */
  editDraw(
    id: string,
    drawingType: DrawingType,
    positions: Cartesian3[],
    callback: (res: IDrawCallback) => void
  ) {
    // 编辑的实体
    const drawEntity = this.drewEntities.entities.getById(id)
    const markPointId = `markPoints_${id}`
    const markPointsDataSources = this.markPoints.getByName(markPointId)
    const markPointsDataSource
        = markPointsDataSources
          && markPointsDataSources.length > 0
          && markPointsDataSources[0]

    // 移动的点
    let activeEntity: Entity | undefined
    // 提示标签
    let labelEntity: Entity | undefined

    if (+drawingType !== DrawingType.Point) {
      if (!markPointsDataSource) return
      // 显示顶点
      markPointsDataSource.show = true
    }

    this.updateActiveEntity(drawEntity, drawingType, positions, true)

    try {
      // 监听鼠标左键
      this.mouseHandlerManager.addLeftMouseDownHandler(
        `drawLeftDown_${this.guid}`,
        ({ screenPosition }: { screenPosition: Cartesian2 }) => {
          activeEntity = this.viewer.scene.pick(screenPosition).id

          if (!defined(activeEntity)) return

          this.viewer.container.style.cursor = "pointer"

          if (
            +drawingType === DrawingType.Point
            || activeEntity?.name === "drawMarkPoint"
          ) {
            // 设置地图不可拖拽
            this.viewer.scene.screenSpaceCameraController.enableRotate = false
          }
        }
      )

      // 鼠标移动事件回调处理
      // 移除上一个实体，添加新实体
      const throttle = _.throttle((screenPosition: Cartesian2) => {
        // 贴地坐标or空间坐标
        const position
          = screenPosition
            && MathUtils.getSurfaceCartesian3(this.viewer, screenPosition)

        // 若无有效坐标
        if (!position) return
        // 绘制移动实体
        if (!defined(position)) return

        if (labelEntity) {
          labelEntity.position = new ConstantPositionProperty(position)
        } else {
          labelEntity = this.createLabel(createGuid(), position)
          this.viewer.entities.add(labelEntity)
        }

        if (!defined(activeEntity)) return
        if (
          +drawingType !== DrawingType.Point
          && activeEntity
          && activeEntity.name !== "drawMarkPoint"
        ) {
          return
        }

        // 更新实时位置
        activeEntity
        && (activeEntity.position = new ConstantPositionProperty(position))

        if (drawingType !== DrawingType.Point && markPointsDataSource) {
          const positions: Cartesian3[] = []
          // 移动标绘
          markPointsDataSource.entities.values.forEach((item) => {
            let pos = item.position && item.position.getValue(JulianDate.now())
            pos && positions.push(pos)
          })

          this.updateActiveEntity(drawEntity, drawingType, positions, true)
        }
      }, 350)

      // 监听鼠标移动事件
      this.mouseHandlerManager.addMouseMoveHandler(
        `drawMouseMove_${this.guid}`,
        ({ screenPosition }) => {
          // 频繁触发
          // 节流处理
          screenPosition && throttle(screenPosition)
        }
      )

      // 监听鼠标抬起事件
      this.mouseHandlerManager.addLeftMouseUpHandler(
        `drawLeftUp_${this.guid}`,
        ({ screenPosition }) => {
          activeEntity = undefined
          this.viewer.container.style.cursor = "default"
        }
      )

      // 监听鼠标抬起事件
      this.mouseHandlerManager.addRightMouseClickHandler(
        `drawRightClick_${this.guid}`,
        ({ screenPosition }) => {
          // 移除左按下、右键单击，鼠标移动事件
          this.mouseHandlerManager.destroyHandlerByKey(
            `drawLeftUp_${this.guid}`
          )
          this.mouseHandlerManager.destroyHandlerByKey(
            `drawLeftDown_${this.guid}`
          )
          this.mouseHandlerManager.destroyHandlerByKey(
            `drawMouseMove_${this.guid}`
          )
          this.mouseHandlerManager.destroyHandlerByKey(
            `drawRightClick_${this.guid}`
          )

          const positions: Cartesian3[] = []
          this.viewer.scene.screenSpaceCameraController.enableRotate = true

          if (+drawingType === DrawingType.Point) {
            let pos
              = drawEntity
                && drawEntity.position
                && drawEntity.position.getValue(JulianDate.now())
            pos && positions.push(pos)
          } else {
            markPointsDataSource && (markPointsDataSource.show = false)
            // 移动标绘
            markPointsDataSource
            && markPointsDataSource.entities.values.forEach((item) => {
              let pos
                  = item.position && item.position.getValue(JulianDate.now())
              pos && positions.push(pos)
            })
          }

          this.updateActiveEntity(drawEntity, drawingType, positions, false)
          // 删除提示标签
          this.viewer.entities.removeById(labelEntity?.id)

          let params: { [propName: string]: any } = {}
          if (drawingType == DrawingType.Circle) {
            params.Radius = drawEntity?.ellipse?.semiMajorAxis?._value
          }
          if (drawingType == DrawingType.Ellipse) {
            params.MajorSemiAxis = drawEntity?.ellipse?.semiMajorAxis?._value
            params.MinorSemiAxis = drawEntity?.ellipse?.semiMinorAxis?._value
          }

          callback({
            id,
            position: positions,
            params
          })
        }
      )
    } catch (error) {
      console.error("drawManager:", error)
    }
  }

  /**
   * 创建随鼠标移动的标签
   */
  createLabel(id: string, position: Cartesian3) {
    const shape = new Entity({
      id,
      name: "drawLabel",
      position,
      label: {
        text: "左键按下移动，右键保存",
        fillColor: Color.WHITE,
        showBackground: true,
        backgroundColor: Color.fromCssColorString("rgba(22, 171, 216, 0.5)"),
        font: "14px sans-serif",
        horizontalOrigin: HorizontalOrigin.LEFT,
        pixelOffset: new Cartesian2(10, 0)
      }
    })
    return shape
  }

  /**
   * 更新实体坐标
   * @param drawEntity 当前编辑的实体
   * @param drawingType 实体类型
   * @param positions 最新坐标
   */
  updateActiveEntity(
    drawEntity: Entity | undefined,
    drawingType: DrawingType,
    positions: Cartesian3[],
    isActive: boolean
  ) {
    if (!drawEntity) return
    switch (+drawingType) {
      case DrawingType.Point:
        this.editPoint(drawEntity, positions, isActive)
        break
      case DrawingType.Polyline:
        this.editPolyline(drawEntity, positions, isActive)
        break
      case DrawingType.Rectangle:
        this.editRectangle(drawEntity, positions, isActive)
        break
      case DrawingType.Polygon:
        this.editPolygon(drawEntity, positions, isActive)
        break
      case DrawingType.Circle:
        this.editCircle(drawEntity, positions, isActive)
        break
      case DrawingType.Ellipse:
        this.editEllipse(drawEntity, positions, isActive)
        break
      default:
        break
    }
  }

  /**
   * @descripttion: 编辑点
   * @param entity 点实体
   * @param positions 坐标
   * @param isActive
   * @returns
   * @author: ssr
   */
  private editPoint(
    entity: Entity | undefined,
    positions: Cartesian3[],
    isActive = false
  ) {
    if (!entity) return
    if (!entity.point) return

    let outLineOpacity = 0.035
    let o = false
    if (isActive) {
      entity.point.outlineWidth = new CallbackProperty(() => {
        return 5
      }, false)
      entity.point.outlineColor = new CallbackProperty(() => {
        return (
          o
            ? ((outLineOpacity -= 0.035), outLineOpacity <= 0 && (o = false))
            : ((outLineOpacity = 0.7), (o = true)),
          Color.fromCssColorString("#fff").withAlpha(outLineOpacity)
        )
      }, false)
    } else {
      entity.point.outlineWidth = new CallbackProperty(() => {
        return 0
      }, false)
      entity.position = new ConstantPositionProperty(positions[0])
    }
  }

  /**
   * @descripttion: 编辑折线
   * @param entity 折线实体
   * @param positions 坐标
   * @param isActive
   * @returns
   * @author: ssr
   */
  private editPolyline(
    entity: Entity | undefined,
    positions: Cartesian3[],
    isActive = false
  ) {
    if (!entity) return

    if (entity.polyline) {
      let lastPosition: Cartesian3 | undefined
        = positions[positions.length - 1]
      entity.position = new ConstantPositionProperty(lastPosition)
      entity.polyline.positions = new CallbackProperty(() => {
        return positions
      }, false)
    }
  }

  /**
   * 编辑矩形
   * @param entity 矩形实体
   * @param positions 坐标
   * @param isActive
   * @returns
   * @author: ssr
   */
  private editRectangle(
    entity: Entity | undefined,
    positions: Cartesian3[],
    isActive = false
  ) {
    if (!entity) return

    const center = DrawUtils.getRectangleCenter(positions)
    center && (entity.position = new ConstantPositionProperty(center))

    if (entity.rectangle) {
      entity.rectangle.coordinates = new CallbackProperty(() => {
        const positionsClone = positions.map(car3 => car3.clone())
        const rectangle = CesiumRectangle.fromCartesianArray(positionsClone)
        return rectangle
      }, true)
    }
    if (entity.polyline) {
      entity.polyline.show = new ConstantProperty(isActive)
      entity.polyline.positions = new CallbackProperty(() => {
        const positionsClone = positions.map(car3 => car3.clone())
        const rectangle = CesiumRectangle.fromCartesianArray(positionsClone)
        const eastNorth = Cartesian3.fromRadians(rectangle.east, rectangle.north)
        const westNorth = Cartesian3.fromRadians(rectangle.west, rectangle.north)
        const eastSouth = Cartesian3.fromRadians(rectangle.east, rectangle.south)
        const westSouth = Cartesian3.fromRadians(rectangle.west, rectangle.south)

        return [eastNorth, westNorth, westSouth, eastSouth, eastNorth]
      }, true)
    }
  }

  /**
   * 编辑多边形
   * @param entity 多边形实体
   * @param positions 坐标
   * @param isActive
   * @returns
   * @author: ssr
   */
  private editPolygon(
    entity: Entity,
    positions: Cartesian3[],
    isActive: boolean
  ) {
    if (!entity) return

    const center = DrawUtils.getPolygonCenter(positions, entity)
    center && (entity.position = new ConstantPositionProperty(center))

    if (entity.polygon) {
      entity.polygon.hierarchy = new CallbackProperty(() => {
        const positionsClone = positions.map(car3 => car3.clone())
        const polygonHierarchy = new PolygonHierarchy(positionsClone)
        return polygonHierarchy
      }, true)
    }

    if (entity.polyline) {
      entity.polyline.show = new ConstantProperty(isActive)
      entity.polyline.positions = new CallbackProperty(() => {
        const positionsClone
          = positions.length < 3
            ? positions.map(car3 => car3.clone())
            : positions
                .concat(positions[0].clone())
                .map(car3 => car3.clone())

        return positionsClone
      }, true)
    }
  }

  /**
   * 编辑圆形
   * @param entity 圆形实体
   * @param positions 坐标
   * @param isActive
   * @returns
   * @author: ssr
   */
  private editCircle(
    entity: Entity,
    positions: Cartesian3[],
    isActive: boolean
  ) {
    if (!entity) return

    if (!entity.ellipse) return
    entity.ellipse.semiMajorAxis = new ConstantProperty(
      Cartesian3.distance(positions[0], positions[1])
    )
    entity.ellipse.semiMinorAxis = new ConstantProperty(
      Cartesian3.distance(positions[0], positions[1])
    )
  }

  /**
   * 编辑椭圆
   * @param entity 椭圆实体
   * @param positions 坐标
   * @param isActive
   * @returns
   * @author: ssr
   */
  private editEllipse(
    entity: Entity,
    positions: Cartesian3[],
    isActive: boolean
  ) {
    if (!entity) return

    // 计算长短半径
    let { majorAxis, minorAxis } = DrawUtils.getEllipseRadius(positions)

    if (!entity.ellipse) return

    entity.ellipse.semiMajorAxis = new ConstantProperty(majorAxis)
    entity.ellipse.semiMinorAxis = new ConstantProperty(minorAxis)
  }

  /**
   * 保存失败标识点恢复原坐标
   * @param shapeId
   * @param positions
   */
  resetMarkPoints(
    shapeId: string,
    drawingType: DrawingType,
    positions: Cartesian3[]
  ) {
    if (+drawingType === DrawingType.Point) {
      return
    }
    const markPointId = `markPoints_${shapeId}`
    const markPointsDataSources = this.markPoints.getByName(markPointId)
    const markPointsDataSource: DataSource | boolean
        = markPointsDataSources
          && markPointsDataSources.length > 0
          && markPointsDataSources[0]

    if (markPointsDataSource) {
      markPointsDataSource.entities.values.forEach((entity: Entity, index) => {
        entity.position = new ConstantPositionProperty(positions[index])
      })
    }
  }
}
